using System;
using MathService.MathParser;
using MathService.MathParser.ExpressionTrees.Nodes;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace MathServiceTestProject
{
    [TestClass]
    public class ExpressionParser_Test
    {
        private static Lazy<ExpressionParser> sf_Parser =
            new Lazy<ExpressionParser>(() => new ExpressionParser(), true);

        public TestContext TestContext { get; set; }

        #region Additional test attributes
        // 
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        //[ClassInitialize]
        //public static void MyClassInitialize(TestContext testContext)
        //{
        //}
        //
        //Use ClassCleanup to run code after all tests in a class have run
        //[ClassCleanup]
        //public static void MyClassCleanup()
        //{
        //}
        //
        //Use TestInitialize to run code before running each test
        //[TestInitialize]
        //public void MyTestInitialize()
        //{
        //}
        //
        //Use TestCleanup to run code after each test has run
        //[TestCleanup]
        //public void MyTestCleanup()
        //{
        //}
        //
        #endregion

        [TestMethod, Priority(10)]
        public void MathExpression_Parce_Test()
        {
            var parser = new ExpressionParser();
            const int a = 1;
            const int b = 2;
            var exp = parser.Parse(string.Format("{0}+{1}", a, b));
            var tree = exp.Tree;
            var root = tree.Root;
            Assert.IsTrue(root is AdditionOperatorNode);

            var left = root.Left;
            Assert.IsTrue(left is ConstValueNode);
            var right = root.Right;
            Assert.IsTrue(right is ConstValueNode);

            var leftValue = left as ConstValueNode;
            Assert.AreEqual(a, leftValue.Value);
            var rightValue = right as ConstValueNode;
            Assert.AreEqual(b, rightValue.Value);

            var result = exp.Compute();
            Assert.AreEqual(a + b, result);

            Action<string, double> test = (str, value) =>
            {
                var expr = parser.Parse(str);
                Assert.AreEqual(value, expr.Compute(), expr.ToString());
            };

            test("2+2", 4);
            test("2*2", 4);

            test("(2+2)", 4);
            test("((2+2))", 4);

            test("2*2+2", 6);
            test("2+2*2", 6);

            test("(2+2)*2", 8);
            test("2*(2+2)", 8);

            test("2^2", 4);
            test("2^2+1", 5);
            test("1+2^2", 5);
            test("2^2*3", 12);
            test("3*2^2", 12);
            test("2^(1+2)", 8);
            test("2^(2*5)", 1024);
            test("(1+1)^(2*5)", 1024);

            test("2.5 * 2", 5);
            test("2.5 - 0.5", 2);
            test("0.1 / 2", 0.05);
            test("0.9 / 0.1", 9);

            test("0 + 0.0", 0);
            test("2 * 0.0", 0);

            test("-1 + 1", -1 + 1);
            test("2 + -1", 2 + -1);
            test("2 * -1", 2 * -1);
            test("2 * (-1)", 2 * (-1));
            test("2 + -1 - -5", 2 + -1 - -5);
            test("+2 - -3 * -5", +2 - -3 * -5);
            test("-2/-1", 2);
            test("(-4-4*-2)^2^-1", 2);


        }

        [TestMethod, Priority(5)]
        public void MathExpression_OperatorPriority_Test()
        {
            var parser = new ExpressionParser();
            var exp = parser.Parse("2+2*2");
            var tree = exp.Tree;
            var root = tree.Root;
            Assert.IsTrue(root is AdditionOperatorNode);
            Assert.IsTrue(root.Left is ConstValueNode);
            Assert.IsTrue(root.Right is MultiplicationOperatorNode);
            Assert.IsTrue(root.Right.Left is ConstValueNode);
            Assert.IsTrue(root.Right.Right is ConstValueNode);

            var result = exp.Compute();
            Assert.AreEqual(6, result);
        }

        [TestMethod, Priority(5)]
        public void MathExpression_Bracket_Test()
        {
            var parser = new ExpressionParser();
            var exp = parser.Parse("(2+2)*2");
            var tree = exp.Tree;
            var root = tree.Root;
            Assert.IsTrue(root is MultiplicationOperatorNode);
            Assert.IsTrue(root.Left is ComputedBracketNode);
            Assert.IsTrue(root.Left.Left is AdditionOperatorNode);
            Assert.IsTrue(root.Left.Left.Left is ConstValueNode);
            Assert.IsTrue(root.Left.Left.Right is ConstValueNode);
            Assert.IsTrue(root.Right is ConstValueNode);

            var result = exp.Compute();
            Assert.AreEqual(8, result);
        }

        [TestMethod]
        public void ExpressionTree_ValueTestNode_Test()
        {
            var parser = new ExpressionParser();
            var exp = parser.Parse("2");
            var root = exp.Tree.Root;
            Assert.IsTrue(root is ConstValueNode);
            var value = (ConstValueNode)root;
            Assert.AreEqual(2, value.Value);
            Assert.AreEqual(2, exp.Compute());

            exp = parser.Parse("2.2");
            root = exp.Tree.Root;
            Assert.IsTrue(root is ConstValueNode);
            value = (ConstValueNode)root;
            Assert.AreEqual(2.2, value.Value);
            Assert.AreEqual(2.2, exp.Compute());
        }

        [TestMethod]
        public void ExpressionTree_PrefixUnaryOperator_Test()
        {
            var parser = new ExpressionParser();
            var exp = parser.Parse("-2");
            var root = exp.Tree.Root;
            Assert.IsTrue(root is SubstractionOperatorNode);
            Assert.AreEqual(-2, exp.Compute());

            exp = parser.Parse("-2+3");
            root = exp.Tree.Root;
            Assert.IsTrue(root is AdditionOperatorNode);
            Assert.IsTrue(root.Left is SubstractionOperatorNode);
            Assert.IsTrue(root["l"].Left == null);
            Assert.IsTrue(root["l"].Right is ConstValueNode);
            Assert.IsTrue(root.Right is ConstValueNode);
            Assert.AreEqual(1, exp.Compute());

            exp = parser.Parse("2*-1");
            root = exp.Tree.Root;
            Assert.IsTrue(root is MultiplicationOperatorNode);
            Assert.IsTrue(root.Left is ConstValueNode);
            Assert.IsTrue(root.Right is SubstractionOperatorNode);
            Assert.IsTrue(root["r"].Left == null);
            Assert.IsTrue(root["r"].Right is ConstValueNode);
            Assert.AreEqual(-2, exp.Compute());

            Assert.AreEqual(2, parser.Parse("4^2^-1").Compute());

        }
    }
}